`define TX_FIFO_DEPTH      4'd16
`define RX_FIFO_DEPTH      4'd16
`define TX_FIFO_ADDR_W     $clog2(`TX_FIFO_DEPTH)
`define RX_FIFO_ADDR_W     $clog2(`TX_FIFO_DEPTH)
`define TFL_W              `TX_FIFO_ADDR_W+1
`define RFL_W              `RX_FIFO_ADDR_W+1

module lpuart_regfile #(
  parameter tx_data_width = 9,
  parameter rx_data_width = 12
) (
  input                      pclk,
  input                      presetn,

  input                      wr_en,
  input                      rd_en,
  input      [5:0]           reg_addr,
  input      [31:0]          ipwdata,
  output reg [31:0]          iprdata,

  output                     rxfnf,

  // dma
  input                      dma_tx_ack,
  input                      dma_rx_ack,
  output                     dma_tx_req,
  output                     dma_rx_req,

  // lpuart_fifo
  output                     tx_push,
  output [tx_data_width-1:0] tx_push_data,
  input                      tx_wempty,
  input                      tx_wfull,
  input                      tx_wone_eighth_thr,
  input                      tx_wone_quarter_thr,
  input                      tx_wone_half_thr,
  input                      tx_wthree_quarter_thr,
  input                      tx_wseven_eighth_thr,

  output                     rx_pop,
  input  [rx_data_width-1:0] rx_pop_data,
  input                      rx_rempty,
  input                      rx_rfull,
  input                      rx_rone_eighth_thr,
  input                      rx_rone_quarter_thr,
  input                      rx_rone_half_thr,
  input                      rx_rthree_quarter_thr,
  input                      rx_rseven_eighth_thr,

  // lpuart_bclk_gen
  output reg [3:0]           clock_psc,
  output     [19:0]          baud_rate,
  output                     ue,

  // lpuart_rx & lpuart_tx
  output                     stop_1bit,
  output                     stop_2bit,
  output                     frame_7bit,
  output                     frame_8bit,
  output                     frame_9bit,
  output                     pce,
  output                     ps,
  output                     hdsel,
  output                     msbfirst,
  output                     fifo_en,

  // lpuart_rx
  input     [11:0]           rx_data,
  input                      rwu,
  input                      lp_wake_up,
  input                      rx_busy,
  input                      rx_finish,

  output                     rx_en,
  output                     rts_en,
  output                     mmrq,
  output                     wake_addrm7,
  output     [7:0]           wake_addr,
  output                     wake_method,
  output                     uesm,
  output                     clr_rxne,
  output                     clr_idle,
  output                     lp_wus,

  // lpuart_tx
  input                      clr_sbk,
  input                      tx_pop_sync,
  input                      tx_finish,

  output                     tx_start,
  output                     tx_en,
  output    [8:0]            tx_data_reg,
  output                     txfrq,
  output                     sbkrq,
  output                     cts_en,
  output                     clr_cts,

  // lpuart_pin_proc
  input                      cts_n,
  output                     txinv,
  output                     rxinv,
  output                     swap_tx_rx
);

parameter LPUART_CR1_ADDR   = 8'h00;
parameter LPUART_CR2_ADDR   = 8'h04;
parameter LPUART_CR3_ADDR   = 8'h08;
parameter LPUART_BRR_ADDR   = 8'h0C;
// 8'h10
// 8'h14
parameter LPUART_RQR_ADDR   = 8'h18;
parameter LPUART_ISR_ADDR   = 8'h1C;
parameter LPUART_ICR_ADDR   = 8'h20;
parameter LPUART_RDR_ADDR   = 8'h24;
parameter LPUART_TDR_ADDR   = 8'h28;
parameter LPUART_PRESC_ADDR = 8'h2C;



wire parity_err, frame_err, noise_err;
wire tx_finish_edge;

reg  [11:0] lpuart_rdr_r;
reg  [11:0] rx_valid_data;

// fifo state
reg dly_fifo_en;
reg [`TFL_W-1:0] tx_fifo_level_cnt;
reg [`RFL_W-1:0] rx_fifo_level_cnt;
reg              tdr_hold_empty;
reg              rdr_hold_full;


// addr decoder
wire lpuart_cr1_en   = (reg_addr == (LPUART_CR1_ADDR>>2));
wire lpuart_cr2_en   = (reg_addr == (LPUART_CR2_ADDR>>2));
wire lpuart_cr3_en   = (reg_addr == (LPUART_CR3_ADDR>>2));
wire lpuart_brr_en   = (reg_addr == (LPUART_BRR_ADDR>>2));
wire lpuart_rqr_en   = (reg_addr == (LPUART_RQR_ADDR>>2));
wire lpuart_isr_en   = (reg_addr == (LPUART_ISR_ADDR>>2));
wire lpuart_icr_en   = (reg_addr == (LPUART_ICR_ADDR>>2));
wire lpuart_rdr_en   = (reg_addr == (LPUART_RDR_ADDR>>2));
wire lpuart_tdr_en   = (reg_addr == (LPUART_TDR_ADDR>>2));
wire lpuart_presc_en = (reg_addr == (LPUART_PRESC_ADDR>>2));

// write enbale
wire lpuart_cr1_we   = lpuart_cr1_en   & wr_en;
wire lpuart_cr2_we   = lpuart_cr2_en   & wr_en;
wire lpuart_cr3_we   = lpuart_cr3_en   & wr_en;
wire lpuart_brr_we   = lpuart_brr_en   & wr_en;
wire lpuart_rqr_we   = lpuart_rqr_en   & wr_en;
wire lpuart_isr_we   = lpuart_isr_en   & wr_en;
wire lpuart_icr_we   = lpuart_icr_en   & wr_en;
wire lpuart_rdr_we   = lpuart_rdr_en   & wr_en;
wire lpuart_tdr_we   = lpuart_tdr_en   & wr_en;
wire lpuart_presc_we = lpuart_presc_en & wr_en;

// read enbale
wire lpuart_cr1_re   = lpuart_cr1_en   & rd_en;
wire lpuart_cr2_re   = lpuart_cr2_en   & rd_en;
wire lpuart_cr3_re   = lpuart_cr3_en   & rd_en;
wire lpuart_brr_re   = lpuart_brr_en   & rd_en;
wire lpuart_rqr_re   = lpuart_rqr_en   & rd_en;
wire lpuart_isr_re   = lpuart_isr_en   & rd_en;
wire lpuart_icr_re   = lpuart_icr_en   & rd_en;
wire lpuart_rdr_re   = lpuart_rdr_en   & rd_en;
wire lpuart_tdr_re   = lpuart_tdr_en   & rd_en;
wire lpuart_presc_re = lpuart_presc_en & rd_en;



reg cr1_fifoen_r;
reg cr1_rxffie_r;
reg cr1_txfeie_r;
reg cr1_ue_r;
reg cr1_wordLength_M1_r;
reg cr1_wordLength_M0_r;
reg [4:0] cr1_deat_r;
reg [4:0] cr1_dedt_r;
reg cr1_cmie_r;
reg cr1_mmie_r;
reg cr1_wake_r;
reg cr1_pce_r;
reg cr1_ps_r;
reg cr1_peie_r;
reg cr1_txfnfie_r;
reg cr1_tcie_r;
reg cr1_rxfneie_r;
reg cr1_idleie_r;
reg cr1_te_r;
reg cr1_re_r;
reg cr1_uesm_r;

always @ (posedge pclk, negedge presetn) begin:LPUART_CR1_PROC
  if(!presetn)begin
    cr1_rxffie_r <= 1'b0;
    cr1_txfeie_r <= 1'b0;
    cr1_fifoen_r <= 1'b0;
    cr1_ue_r <= 1'b0;
    cr1_wordLength_M1_r <= 1'b0;
    cr1_wordLength_M0_r <= 1'b0;
    cr1_deat_r <= 5'b0;
    cr1_dedt_r <= 5'b0;
    cr1_cmie_r <= 1'b0;
    cr1_mmie_r <= 1'b0;
    cr1_wake_r <= 1'b0;
    cr1_pce_r <= 1'b0;
    cr1_ps_r  <= 1'b0;
    cr1_peie_r <= 1'b0;
    cr1_txfnfie_r <= 1'b0;
    cr1_tcie_r <= 1'b0;
    cr1_rxfneie_r <= 1'b0;
    cr1_idleie_r <= 1'b0;
    cr1_te_r <= 1'b0;
    cr1_re_r <= 1'b0;
    cr1_uesm_r <= 1'b0;
  end else begin
    if(lpuart_cr1_we)begin
      cr1_rxffie_r <= ipwdata[31] & (ipwdata[29]|cr1_fifoen_r); // fifo mode
      cr1_txfeie_r <= ipwdata[30] & (ipwdata[29]|cr1_fifoen_r); // fifo mode
      cr1_fifoen_r <= ipwdata[29];
      if(~(ipwdata[0]|cr1_ue_r)) begin
        cr1_wordLength_M1_r <= ipwdata[28];
        cr1_wordLength_M0_r <= ipwdata[12];
        cr1_deat_r <= ipwdata[25:21];
        cr1_dedt_r <= ipwdata[20:16];
        cr1_wake_r <= ipwdata[11];
        cr1_pce_r <=  ipwdata[10];
        cr1_ps_r  <=  ipwdata[9];
      end
      cr1_cmie_r <= ipwdata[14];
      cr1_mmie_r <= ipwdata[13];
      cr1_peie_r <= ipwdata[8];
      cr1_txfnfie_r <= ipwdata[7];
      cr1_tcie_r <= ipwdata[6];
      cr1_rxfneie_r <= ipwdata[5];
      cr1_idleie_r  <= ipwdata[4];
      cr1_te_r <= ipwdata[3];
      cr1_re_r <= ipwdata[2];
      cr1_uesm_r <= ipwdata[1];
      cr1_ue_r <= ipwdata[0];
    end
  end
end

assign fifo_en = cr1_fifoen_r;
assign wake_method = cr1_wake_r;
assign pce = cr1_pce_r;
assign ps = cr1_ps_r;
assign tx_en = cr1_te_r;
assign rx_en = cr1_re_r;
assign uesm = cr1_uesm_r;
assign ue   = cr1_ue_r;

wire [1:0] cr1_wordLength = {cr1_wordLength_M1_r, cr1_wordLength_M0_r};
assign frame_7bit = (cr1_wordLength == 2'b10);
assign frame_8bit = (cr1_wordLength == 2'b00)||(cr1_wordLength == 2'b11);
assign frame_9bit = (cr1_wordLength == 2'b01);


wire [31:0] lpuart_cr1 = {cr1_rxffie_r, cr1_txfeie_r, cr1_fifoen_r, cr1_wordLength_M1_r, 1'b0, 1'b0,
                          cr1_deat_r, cr1_dedt_r, 1'b0, cr1_cmie_r, cr1_mmie_r, cr1_wordLength_M0_r, 
                          cr1_wake_r, cr1_pce_r, cr1_ps_r, cr1_peie_r, cr1_txfnfie_r, cr1_tcie_r, 
                          cr1_rxfneie_r, cr1_idleie_r, cr1_te_r, cr1_re_r, cr1_uesm_r, cr1_ue_r};


reg [7:0] cr2_add_r;
reg cr2_msbfirst_r;
reg cr2_dataivn_r;
reg cr2_txinv_r;
reg cr2_rxinv_r;
reg cr2_swap_r;
reg [1:0] cr2_stop_r;
reg cr2_addM7_r;

always @ (posedge pclk, negedge presetn) begin:LPUART_CR2_PROC
  if(!presetn)begin
    cr2_add_r <= 8'h0;
    cr2_msbfirst_r <= 1'b0;
    cr2_dataivn_r <= 1'b0;
    cr2_txinv_r <= 1'b0;
    cr2_rxinv_r <= 1'b0;
    cr2_swap_r <= 1'b0;
    cr2_stop_r <= 1'b0;
    cr2_addM7_r <= 1'b0;
  end else begin
    if(lpuart_cr2_we & (~cr1_ue_r))begin
      cr2_add_r <= ipwdata[31:24];
      cr2_msbfirst_r <= ipwdata[19];
      cr2_dataivn_r <= ipwdata[18];
      cr2_txinv_r <= ipwdata[17];
      cr2_rxinv_r <= ipwdata[16];
      cr2_swap_r  <= ipwdata[15];
      cr2_stop_r <= ipwdata[13:12];
      cr2_addM7_r <= ipwdata[4];
    end
  end
end

assign wake_addrm7 = cr2_addM7_r;
assign wake_addr   = cr2_add_r;

assign stop_1bit = (cr2_stop_r == 2'b00) || (cr2_stop_r == 2'b01) || (cr2_stop_r == 2'b11) ;
assign stop_2bit = (cr2_stop_r == 2'b10);

assign txinv = cr2_txinv_r;
assign rxinv = cr2_rxinv_r;
assign swap_tx_rx = cr2_swap_r;
assign msbfirst = cr2_msbfirst_r;


wire [31:0] lpuart_cr2 = {cr2_add_r, 4'h0, cr2_msbfirst_r, cr2_dataivn_r, cr2_txinv_r, 
                          cr2_rxinv_r, 1'b0, cr2_stop_r, 7'h0, cr2_addM7_r, 4'h0};

reg [2:0] cr3_txftcfg_r;
reg cr3_rxftie_r;
reg [2:0] cr3_rxftcfg_r;
reg cr3_txftie_r;
reg cr3_wufie_r;
reg [1:0] cr3_wus_r;
reg cr3_dep_r;
reg cr3_dem_r;
reg cr3_ddre_r;
reg cr3_ovrdis_r;
reg cr3_ctsie_r;
reg cr3_ctse_r;
reg cr3_rtse_r;
reg cr3_dmat_r;
reg cr3_dmar_r;
reg cr3_hdsel_r;
reg cr3_eie_r;

always @ (posedge pclk, negedge presetn) begin:LPUART_CR3_PROC
  if(!presetn)begin
    cr3_txftcfg_r <= 3'h0;
    cr3_rxftie_r <= 1'b0;
    cr3_rxftcfg_r <= 1'b0;
    cr3_txftie_r <= 1'b0;
    cr3_wufie_r <= 1'b0;
    cr3_wus_r <= 2'h0;
    cr3_dep_r <= 1'b0;
    cr3_dem_r <= 1'b0;
    cr3_ddre_r <= 1'b0;
    cr3_ovrdis_r <= 1'b0;
    cr3_ctsie_r <= 1'b0;
    cr3_ctse_r <= 1'b0;
    cr3_rtse_r <= 1'b0;
    cr3_dmat_r <= 1'b0;
    cr3_dmar_r <= 1'b0;
    cr3_hdsel_r <= 1'b0;
    cr3_eie_r <= 1'b0;
  end else begin
    if(lpuart_cr3_we)begin
      if(~cr1_ue_r)begin
        cr3_wus_r <= ipwdata[21:20];
        cr3_dep_r <= ipwdata[15];
        cr3_dem_r <= ipwdata[14];
        cr3_ddre_r <= ipwdata[13];
        cr3_ovrdis_r <= ipwdata[12];
        cr3_ctse_r <= ipwdata[9];
        cr3_rtse_r <= ipwdata[8];
        cr3_hdsel_r <= ipwdata[3];
      end
      cr3_txftcfg_r <= ipwdata[31:29];
      cr3_rxftie_r <= ipwdata[28];
      cr3_rxftcfg_r <= ipwdata[27:25];
      cr3_txftie_r <= ipwdata[23];
      cr3_wufie_r <= ipwdata[22];
      cr3_ctsie_r <= ipwdata[10];
      cr3_dmat_r <= ipwdata[7];
      cr3_dmar_r <= ipwdata[6];
      cr3_eie_r <= ipwdata[0];
    end
  end
end

assign cts_en = cr3_ctse_r;
assign rts_en = cr3_rtse_r & cr1_ue_r;
assign hdsel = cr3_hdsel_r;
assign lp_wus = cr3_wus_r;


wire [31:0] lpuart_cr3 = {cr3_txftcfg_r, cr3_rxftie_r, cr3_rxftcfg_r, 1'b0, cr3_txftie_r, cr3_wufie_r, cr3_wus_r, 4'h0,
                          cr3_dep_r, cr3_dem_r, cr3_ddre_r, cr3_ovrdis_r, 1'b0, cr3_ctsie_r, cr3_ctse_r, cr3_rtse_r, 
                          cr3_dmat_r, cr3_dmar_r, 2'h0, cr3_hdsel_r, 2'h0, cr3_eie_r};

reg [19:0] lpuart_brr_r;                    
always @ (posedge pclk, negedge presetn) begin:LPUART_BRR_PROC
  if(!presetn)begin
    lpuart_brr_r <= 20'h300;
  end else begin
    if(lpuart_brr_we && (!cr1_ue_r))begin
      lpuart_brr_r <= ipwdata[19:0];
    end
  end
end

assign baud_rate = lpuart_brr_r;
wire [31:0] lpuart_brr = {12'h0, lpuart_brr_r};


wire rqr_txfrq = lpuart_rqr_we & ipwdata[4];
wire rqr_rxfrq = lpuart_rqr_we & ipwdata[3];

reg rqr_mmrq_r;
always @ (posedge pclk, negedge presetn) begin:LPUART_RQR_MMRQ_PROC
  if(!presetn)
    rqr_mmrq_r <= 1'b0;
  else if(rqr_mmrq_r & tx_finish)
    rqr_mmrq_r <= 1'b0;
  else if(lpuart_rqr_we & ipwdata[2])
    rqr_mmrq_r <= 1'b1;
end

reg rqr_sbkrq_r;
always @ (posedge pclk, negedge presetn) begin:LPUART_RQR_SBKRQ_PROC
  if(!presetn)
    rqr_sbkrq_r <= 1'b0;
  else if(rqr_sbkrq_r & tx_finish)
    rqr_sbkrq_r <= 1'b0;
  else if(lpuart_rqr_we & ipwdata[1])
    rqr_sbkrq_r <= 1'b1;
end

assign txfrq = rqr_txfrq;
assign rxfrq = rqr_rxfrq;
assign mmrq  = rqr_mmrq_r;
assign sbkrq = rqr_sbkrq_r;

wire [31:0] lpuart_rqr = {32'h0};

// LPUART_ISR_PROC
// fifo mode
// register mode

reg tx_threshold_flag;
always@(*)begin
    case(cr3_txftcfg_r)
      3'b000:  tx_threshold_flag = tx_wseven_eighth_thr;  // 1/8
      3'b001:  tx_threshold_flag = tx_wone_quarter_thr;   // 1/4
      3'b110:  tx_threshold_flag = tx_wone_half_thr;      // 1/2
      3'b011:  tx_threshold_flag = tx_wthree_quarter_thr; // 3/4
      3'b100:  tx_threshold_flag = tx_wseven_eighth_thr;  // 7/8
      3'b101:  tx_threshold_flag = tx_wempty; // 0
      default: tx_threshold_flag = 1'b0; 
    endcase
end

reg isr_txft_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_txft_r <= 1'b0;
  else if(cr1_fifoen_r)begin
    isr_txft_r <= tx_threshold_flag;
  end
end

reg rx_threshold_flag;
always@(*)begin
    case(cr3_rxftcfg_r)
      3'b000:  rx_threshold_flag = rx_rone_eighth_thr; // 1/8
      3'b001:  rx_threshold_flag = rx_rone_quarter_thr; // 1/4
      3'b110:  rx_threshold_flag = rx_rone_half_thr; // 1/2
      3'b011:  rx_threshold_flag = rx_rthree_quarter_thr; // 3/4
      3'b100:  rx_threshold_flag = rx_rseven_eighth_thr; // 7/8
      3'b101:  rx_threshold_flag = rx_rfull; // full
      default: rx_threshold_flag = 1'b0; 
    endcase
end

reg isr_rxft_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_rxft_r <= 1'b0;
  else if(cr1_fifoen_r)begin
    isr_rxft_r <= rx_threshold_flag;
  end
end

reg isr_rxff_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_rxff_r <= 1'b0;
  else if(cr1_fifoen_r)begin
    if(rx_rfull)
      isr_rxff_r <= 1'b1;
    else
      isr_rxff_r <= 1'b0;
  end
  else
    isr_rxff_r <= 1'b0;
end


reg isr_txfe_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_txfe_r <= 1'b0;
  else if(cr1_fifoen_r)begin
    if(tx_wempty)
      isr_txfe_r <= 1'b1;
    else
      isr_txfe_r <= 1'b0;
  end
  else
    isr_txfe_r <= 1'b0;
end

wire isr_reack = 1'b0; // uart_clk rx_en feedback
wire isr_track = 1'b0; // uart_clk tx_en feedback

reg isr_wuf_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_wuf_r <= 1'b0;
  else if(!cr1_uesm_r)
    isr_wuf_r <= 1'b0;
  else if(lp_wake_up)
    isr_wuf_r <= 1'b1;
end

reg isr_rwu_r;
wire wake_ilde_line = (wake_method == 1'b0);
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_rwu_r <= 1'b0;
  else if(wake_ilde_line)begin
    if(lpuart_rqr_we & ipwdata[2])
      isr_rwu_r <= 1'b0;
    else if(lpuart_rqr_we & (~ipwdata[2]))
      isr_rwu_r <= 1'b1;
  end
  else if(rwu)
    isr_rwu_r <= 1'b1;
end

reg isr_sbkf_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_sbkf_r <= 1'b0;
  else if(isr_sbkf_r & tx_finish)
    isr_sbkf_r <= 1'b0;
  else if(lpuart_rqr_we & ipwdata[1])
    isr_sbkf_r <= 1'b1;
end

wire char_match = (lpuart_rdr_r[7:0] == cr2_add_r[7:0]);

reg isr_cmf_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_cmf_r <= 1'b0;
  else if(lpuart_icr_we & ipwdata[17])
    isr_cmf_r <= 1'b0;
  else if(char_match)
    isr_cmf_r <= 1'b1;
end

wire isr_busy = rx_busy;

wire isr_cts = ~cts_n;

reg dly_cts_n;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    dly_cts_n <= 1'b1;
  else
    dly_cts_n <= cts_n;
end
wire cts_n_edge = cts_n ^ dly_cts_n;

reg isr_ctsif_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_ctsif_r <= 1'b1;
  else if(lpuart_icr_we & ipwdata[9])
    isr_ctsif_r <= 1'b0;
  else if(cts_n_edge)
    isr_ctsif_r <= 1'b1;
end


wire isr_txfnf = cr1_fifoen_r ? ~tx_wfull : 1'b1;

reg tx_in_prog;
always @ (posedge pclk, negedge presetn) begin
  if(!presetn)
    tx_in_prog <= 1'b0;
  else if(tx_start)
    tx_in_prog <= 1'b1;
  else if(tx_finish_edge)
    tx_in_prog <= 1'b0;
end

reg isr_txe;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
      isr_txe <= 1'b1;
  else if(!cr1_fifoen_r)begin
    if(tx_finish_edge)
      isr_txe <= 1'b1;
    else if(lpuart_tdr_we)
      isr_txe <= 1'b0;
  end else
    isr_txe <= 1'b0;
end

wire isr_txe_txfnf = cr1_fifoen_r ? isr_txfnf : isr_txe;

reg isr_tc_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_tc_r = 1'b0;
  else if(lpuart_icr_we & ipwdata[6])
    isr_tc_r = 1'b0;
  else if(lpuart_tdr_we)
    isr_tc_r = 1'b0;
  else if(tx_finish & isr_txe)
    isr_tc_r = 1'b1;
end

reg isr_rxne_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_rxne_r = 1'b0;
  else if(lpuart_rqr_we & ipwdata[3])
    isr_rxne_r = 1'b0;
  else if(lpuart_rdr_re)
    isr_rxne_r = 1'b0;
  else if(rx_finish && (!cr1_fifoen_r))
    isr_rxne_r = 1'b1;
end

reg isr_rxfnf_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_rxfnf_r = 1'b0;
  else if(lpuart_rqr_we & ipwdata[3])
    isr_rxfnf_r = 1'b0;
  else if(rx_rempty)
    isr_rxfnf_r = 1'b0;
  else
    isr_rxfnf_r = 1'b1;
end

assign rxfnf = isr_rxfnf_r;
wire isr_rxne_rxfne = cr1_fifoen_r ? isr_rxfnf_r : isr_rxne_r;

reg idle_line_det;
always@(*)begin
  idle_line_det = 1'b0;
  case({pce, cr1_wordLength})
    3'b000:idle_line_det = (lpuart_rdr_r[7:0] == 8'hff);
    3'b001:idle_line_det = (lpuart_rdr_r[8:0] == 9'h1ff);
    3'b010:idle_line_det = (lpuart_rdr_r[6:0] == 8'h7f);
    3'b100:idle_line_det = (lpuart_rdr_r[6:0] == 8'h7f);
    3'b101:idle_line_det = (lpuart_rdr_r[7:0] == 8'hff);
    3'b110:idle_line_det = (lpuart_rdr_r[5:0] == 8'h3f);
    default:idle_line_det = 1'b0;
  endcase
end

reg isr_idle_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_idle_r = 1'b0;
  else if(lpuart_icr_we & ipwdata[4])
    isr_idle_r = 1'b0;
  else if(isr_rwu_r) // mute mode
    isr_idle_r = 1'b0;
  else if(idle_line_det & (~isr_idle_r)) // find one time
    isr_idle_r = 1'b1;
end

wire overrun_err = (isr_rxff_r && rx_finish);

reg isr_ore_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_ore_r = 1'b0;
  else if(lpuart_icr_we & ipwdata[3])
    isr_ore_r = 1'b0;
  else if(cr3_ovrdis_r)
    isr_ore_r = 1'b0;
  else if(overrun_err)
    isr_ore_r = 1'b1;
end

reg isr_ne_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_ne_r = 1'b0;
  else if(lpuart_icr_we & ipwdata[2])
    isr_ne_r = 1'b0;
  else if(noise_err)
    isr_ne_r = 1'b1;
end

reg isr_fe_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_fe_r = 1'b0;
  else if(lpuart_icr_we & ipwdata[1])
    isr_fe_r = 1'b0;
  else if(frame_err)
    isr_fe_r = 1'b1;
end

reg isr_pe_r;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    isr_pe_r = 1'b0;
  else if(lpuart_icr_we & ipwdata[0])
    isr_pe_r = 1'b0;
  else if(parity_err)
    isr_pe_r = 1'b1;
end


wire [31:0] lpuart_isr = {4'h0, isr_txft_r, 1'b0, isr_rxft_r, isr_rxff_r, isr_txfe_r, isr_reack, isr_wuf_r, 
                         isr_rwu_r, isr_sbkf_r, isr_cmf_r, isr_busy, 5'h0, isr_cts, isr_ctsif_r, 1'b0, 
                         isr_txe_txfnf,  isr_tc_r, isr_rxne_rxfne, isr_idle_r, isr_ore_r, isr_ne_r, isr_fe_r, isr_pe_r};



// LPUART_ICR                
wire [31:0] lpuart_icr = 32'h0;


always @ (posedge pclk, negedge presetn) begin:LPUART_RDR_PROC
  if(!presetn)begin
    lpuart_rdr_r <= 12'h0;
  end else begin
    if(cr2_dataivn_r)
      lpuart_rdr_r <= ~rx_valid_data;
    else
      lpuart_rdr_r <= rx_valid_data;
  end
end

assign noise_err  = lpuart_rdr_r[11];
assign frame_err  = lpuart_rdr_r[10];
assign parity_err = lpuart_rdr_r[9];

wire [31:0] lpuart_rdr = {20'h0, lpuart_rdr_r};


reg [8:0] lpuart_tdr_r;
always @ (posedge pclk, negedge presetn) begin:LPUART_TDR_PROC
  if(!presetn)begin
    lpuart_tdr_r <= 9'h0;
  end else begin
    if(lpuart_tdr_we & isr_txe_txfnf)begin
      if(cr2_dataivn_r)
        lpuart_tdr_r <= ~ipwdata[8:0];
      else
        lpuart_tdr_r <= ipwdata[8:0];
    end
  end
end

assign tx_data_reg = lpuart_tdr_r;

wire [31:0] lpuart_tdr = {23'h0, lpuart_tdr_r};


reg [3:0] lpuart_presc_r;
always @ (posedge pclk, negedge presetn) begin:LPUART_PRESC_PROC
  if(!presetn)begin
    lpuart_presc_r <= 3'h0;
  end else begin
    if(lpuart_presc_we && (!cr1_ue_r))begin
      lpuart_presc_r <= ipwdata[3:0];
    end
  end
end

wire lpuart_presc = { 28'h0, lpuart_presc_r};
assign clock_psc = lpuart_presc_r; 


always @ (*)begin: IPRDATA_PROC
  iprdata = {32{1'b0}};
  case(1'b1)
    lpuart_cr1_re  : iprdata = lpuart_cr1;
    lpuart_cr2_re  : iprdata = lpuart_cr2;
    lpuart_cr3_re  : iprdata = lpuart_cr3;
    lpuart_brr_re  : iprdata = lpuart_brr;
    lpuart_rqr_re  : iprdata = lpuart_rqr;
    lpuart_isr_re  : iprdata = lpuart_isr; 
    lpuart_icr_re  : iprdata = lpuart_icr;
    lpuart_rdr_re  : iprdata = lpuart_rdr; 
    lpuart_tdr_re  : iprdata = lpuart_tdr;
    lpuart_presc_re: iprdata = lpuart_presc; 
    default: iprdata = 32'h0;
  endcase
end


//-------------
// fifo state
//-------------

assign tx_push = fifo_en ? lpuart_tdr_we & (~tx_wfull) : 1'b0;
assign tx_push_data = fifo_en ? ({9{lpuart_tdr_we}} & ipwdata[8:0]) : 9'h0;

assign rx_pop = fifo_en ? lpuart_rdr_re & (~rx_rempty) : 1'b0;
assign rx_valid_data = fifo_en ? rx_pop_data : rx_data;

//-------------
// tx ctrl
//-------------
reg dly_tx_finish;
always@(posedge pclk, negedge presetn)begin
  if(!presetn)
    dly_tx_finish <= 1'b0;
  else
    dly_tx_finish <= tx_finish;
end
assign tx_finish_edge = tx_finish ^ dly_tx_finish;

assign auto_cts = ~cts_n;
assign tx_start = fifo_en ? 1'b0 : (~tx_in_prog) & auto_cts & (!isr_txe_txfnf);

//-------------
// interrupt
//-------------

assign rxff_irq = cr1_rxffie_r & isr_rxff_r;
assign txfe_irq = cr1_txfeie_r & isr_txfe_r;
assign cm_irq = cr1_cmie_r & isr_cmf_r;
assign pe_irq = cr1_peie_r & isr_pe_r;
assign txfnf_irq = cr1_txfnfie_r & isr_txe_txfnf;
assign tc_irq = cr1_tcie_r & isr_tc_r;
assign rxfne_irq = cr1_rxfneie_r & isr_rxne_rxfne;
assign idle_irq = cr1_idleie_r & isr_idle_r;

assign rxft_irq = cr3_rxftie_r & isr_rxft_r;
assign txft_irq = cr3_txftie_r & isr_txft_r;
assign wuf_irq = cr3_wufie_r & isr_wuf_r;
assign cts_irq = cr3_ctse_r & isr_ctsif_r;
assign error_irq = cr3_eie_r & (isr_fe_r | isr_ore_r | isr_ne_r);

//-------------
// dma
//-------------
assign dma_rx_req  = cr3_dmar_r ? cr3_dmar_r && isr_rxne_rxfne : 1'b0;
assign dma_tx_req  = cr3_dmat_r ? cr3_dmat_r && isr_txe_txfnf  : 1'b0;

endmodule
